package cz.data.common.database.datasource;

import cz.data.common.database.DbDialect;
import cz.data.common.support.db.source.DbQuery;
import cz.data.common.support.db.core.PageResult;
import cz.data.common.database.exception.DataQueryException;
import com.zaxxer.hikari.HikariDataSource;
import cz.data.common.utils.ThrowableUtil;
import lombok.Setter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Setter
public class AbstractJdbcDbQuery implements DbQuery {

    protected DataSource dataSource;

    protected JdbcTemplate jdbcTemplate;

    protected DbDialect dbDialect;

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    @Override
    public Connection getConnection() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new DataQueryException("获取数据库连接出错");
        }
    }

    @Override
    public void close() {
        if (dataSource instanceof HikariDataSource) {
            ((HikariDataSource) dataSource).close();
        } else {
            if (dataSource instanceof AutoCloseable) {
                AutoCloseable closeable = (AutoCloseable)dataSource;
                ThrowableUtil.throwable(closeable::close, false);
            }
        }
    }

    @Override
    public int count(String sql) {
        Integer count = jdbcTemplate.queryForObject(dbDialect.count(sql), Integer.class);
        return Optional.ofNullable(count).orElse(0);
    }

    @Override
    public int count(String sql, Object[] args) {
        Integer count = jdbcTemplate.queryForObject(dbDialect.count(sql), Integer.class, args);
        return Optional.ofNullable(count).orElse(0);
    }

    @Override
    public int count(String sql, Map<String, Object> params) {
        NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        Integer count = namedJdbcTemplate.queryForObject(dbDialect.count(sql), params, Integer.class);
        return Optional.ofNullable(count).orElse(0);
    }

    @Override
    public List<Map<String, Object>> queryList(String sql) {
        return jdbcTemplate.queryForList(sql);
    }

    @Override
    public List<Map<String, Object>> queryList(String sql, Object[] args) {
        return jdbcTemplate.queryForList(sql, args);
    }

    @Override
    public PageResult<Map<String, Object>> queryByPage(String sql, long offset, long size) {
        int total = count(sql);
        String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
        List<Map<String, Object>> records = jdbcTemplate.queryForList(pageSql);
        return new PageResult<>(total, records);
    }

    @Override
    public PageResult<Map<String, Object>> queryByPage(String sql, Object[] args, long offset, long size) {
        int total = count(sql, args);
        String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
        List<Map<String, Object>> records = jdbcTemplate.queryForList(pageSql, args);
        return new PageResult<>(total, records);
    }

    @Override
    public PageResult<Map<String, Object>> queryByPage(String sql, Map<String, Object> params, long offset, long size) {
        int total = count(sql, params);
        String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
        NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        List<Map<String, Object>> records = namedJdbcTemplate.queryForList(pageSql, params);
        return new PageResult<>(total, records);
    }

}
